package com.ushine.web.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ushine.web.base.mapper.UserMapper;
import com.ushine.web.base.model.relation.UserAndRoleDO;
import com.ushine.web.base.model.relation.UserAndUserGroupDO;
import com.ushine.web.base.model.relation.UserGroupAndRoleDO;
import com.ushine.web.base.model.system.RoleDO;
import com.ushine.web.base.model.system.UserDO;
import com.ushine.web.base.model.system.UserGroupDO;
import com.ushine.web.base.service.*;
import com.ushine.web.component.contant.BusinessConstant;
import com.ushine.web.component.exception.ServiceException;
import com.ushine.web.component.exception.ServiceExceptionEnum;
import com.ushine.web.component.page.PageRequestQuery;
import com.ushine.web.security.utils.RsaKeyConfig;
import com.ushine.web.security.utils.RsaUtils;
import com.ushine.web.security.utils.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: yls
 * @Date: 2021/3/12 13:28
 * @Description: 用户管理 -- ServiceImpl
 * @Version 1.0
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IUserService {

    @Resource
    private IRoleService roleService;

    @Resource
    private IUserAndRoleService userAndRoleService;

    @Resource
    private IUserAndUserGroupService userAndUserGroupService;

    @Resource
    private IUserGroupService userGroupService;

    @Resource
    private IUserGroupAndRoleService userGroupAndRoleService;

    @Resource
    RsaKeyConfig rsaKeyConfig;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean insertUser(UserDO user) throws ServiceException {
        UserDO userByUserName = getUserBaseInfo(user.getUsername());
        if (userByUserName != null) {
            throw new ServiceException(ServiceExceptionEnum.EXIST_SAME_NAME_DATA);
        }
        // RSA 解密
        String tempPassword = "123456";
        String temp = "";
        if (!StringUtils.isBlank(user.getPassword())) {
            // 解密前端传回来的加密数据
            temp = RsaUtils.decrypt(user.getPassword(), rsaKeyConfig.getPrivateKeyFile());
            temp = Optional.ofNullable(temp).orElse(tempPassword);
        }
        user.setPassword(new BCryptPasswordEncoder().encode(temp));
        return save(user);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean updateUser(UserDO userDO) {
        return updateById(userDO);
    }


    @Override
    public Boolean deleteUsers(List<String> userIds) {
        return removeByIds(userIds);
    }


    @Override
    @Transactional(readOnly = true)
    public Page<UserDO> listUsersByPage(PageRequestQuery pageRequestQuery) {
        Page<UserDO> page = new Page<>(pageRequestQuery.getPageNum(), pageRequestQuery.getPageSize());
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(UserDO.class, i -> !BusinessConstant.PASSWORD.equals(i.getColumn()) && !"salt".equals(i.getColumn()));
        Page<UserDO> userPage = page(page, queryWrapper);
        List<UserDO> users = userPage.getRecords();
        if (!users.isEmpty()) {
            // 组装userIds
            List<Long> userIds = users.stream().map(UserDO::getId).collect(Collectors.toList());
            // 用户组关联关系
            LambdaQueryWrapper<UserAndUserGroupDO> userAndUserGroupQueryWrapper = new LambdaQueryWrapper<>();
            userAndUserGroupQueryWrapper.in(UserAndUserGroupDO::getUserId, userIds)
                    .select(UserAndUserGroupDO::getUserId, UserAndUserGroupDO::getUserGroupId);
            List<UserAndUserGroupDO> userAndUserGroups = userAndUserGroupService.list(userAndUserGroupQueryWrapper);
            userAndUserGroups = Optional.ofNullable(userAndUserGroups).orElse(new ArrayList<>(0));
            // 分组  userId userGroupId
            Map<Long, Set<Long>> userAndUserGroupMap = userAndUserGroups.stream()
                    .collect(Collectors.groupingBy(UserAndUserGroupDO::getUserId,
                            Collectors.mapping(UserAndUserGroupDO::getUserGroupId, Collectors.toSet())));
            Set<Long> userGroupId = userAndUserGroups.stream().map(UserAndUserGroupDO::getUserGroupId).collect(Collectors.toSet());
            userGroupId = Optional.ofNullable(userGroupId).orElse(new HashSet<>(0));
            userGroupId.add(-1L);
            // 获取用户组
            LambdaQueryWrapper<UserGroupDO> userGroupQueryWrapper = new LambdaQueryWrapper<>();
            userGroupQueryWrapper.in(UserGroupDO::getId, userGroupId)
                    .select(UserGroupDO::getId, UserGroupDO::getUserGroupName);
            List<UserGroupDO> userGroups = userGroupService.list(userGroupQueryWrapper);
            Map<Long, String> userGroupNameMap = userGroups.stream().collect(Collectors.toMap(UserGroupDO::getId, UserGroupDO::getUserGroupName));
            // 设置用户组名称，支持多个
            setUserGroupNames(users, userAndUserGroupMap, userGroupNameMap);
            // 设置用户组关联的角色名称，支持多个
            setUserGroupRoleNames(users, userAndUserGroupMap, userGroups);

            // 用户角色关联关系
            LambdaQueryWrapper<UserAndRoleDO> userRoleQueryWrapper = new LambdaQueryWrapper<>();
            userRoleQueryWrapper.in(UserAndRoleDO::getUserId, userIds)
                    .select(UserAndRoleDO::getUserId, UserAndRoleDO::getRoleId);
            List<UserAndRoleDO> userAndRoleDOList = userIds.isEmpty() ? new ArrayList<>(0) : userAndRoleService.list(userRoleQueryWrapper);

            //获取角色
            List<Long> roleIds = userAndRoleDOList.stream().map(UserAndRoleDO::getRoleId).collect(Collectors.toList());
            List<RoleDO> roles = roleIds.isEmpty() ? new ArrayList<>(0) : roleService.listByIds(roleIds);
            Map<Long, String> roleMap = roles.stream().collect(Collectors.toMap(RoleDO::getId, RoleDO::getName));
            // 设置角色名称，支持多个
            setUserRoleNames(users, userAndRoleDOList, roleMap);
        }
        return userPage;
    }

    /**
     * 功能描述:
     * 〈 设置用户组的多个角色名称 〉
     *
     * @param users               用户集合
     * @param userAndUserGroupMap 用户和用户组关联关系
     * @param userGroups          用户组集合
     * @return : void
     * @author : yls
     * @date : 2022/2/16 14:36
     */
    private void setUserGroupRoleNames(List<UserDO> users, Map<Long, Set<Long>> userAndUserGroupMap, List<UserGroupDO> userGroups) {
        // 获取用户组的关联关系
        List<Long> userGroupIds = userGroups.stream().map(UserGroupDO::getId).collect(Collectors.toList());
        userGroupIds = Optional.ofNullable(userGroupIds).orElse(new ArrayList<>(0));
        userGroupIds.add(-1L);
        LambdaQueryWrapper<UserGroupAndRoleDO> userGroupAndRoleQueryWrapper = new LambdaQueryWrapper<>();
        userGroupAndRoleQueryWrapper.in(UserGroupAndRoleDO::getUserGroupId, userGroupIds)
                .select(UserGroupAndRoleDO::getRoleId, UserGroupAndRoleDO::getUserGroupId);
        List<UserGroupAndRoleDO> userGroupAndRoles = userGroupAndRoleService.list(userGroupAndRoleQueryWrapper);
        // 组装 userGoupId roleId
        Map<Long, Set<Long>> userGoupAndRoleIdMap = userGroupAndRoles.stream().collect(Collectors.groupingBy(UserGroupAndRoleDO::getUserGroupId,
                Collectors.mapping(UserGroupAndRoleDO::getRoleId, Collectors.toSet())));
        Set<Long> roleGroupIds = userGroupAndRoles.stream().map(UserGroupAndRoleDO::getRoleId).collect(Collectors.toSet());
        roleGroupIds = Optional.ofNullable(roleGroupIds).orElse(new HashSet<>(0));
        roleGroupIds.add(-1L);
        // 获取用户组的多个角色
        LambdaQueryWrapper<RoleDO> roleQueryWrapper = new LambdaQueryWrapper<>();
        roleQueryWrapper.in(RoleDO::getId, roleGroupIds)
                .select(RoleDO::getId, RoleDO::getName);
        List<RoleDO> roleGroupList = roleService.list(roleQueryWrapper);
        roleGroupList = Optional.ofNullable(roleGroupList).orElse(new ArrayList<>(0));
        Map<Long, String> userRoleNameMap = roleGroupList.stream().collect(Collectors.toMap(RoleDO::getId, RoleDO::getName));

        Map<Long, Set<String>> userAndRoleNameMap = new HashMap<>(1);
        // 组装userID 和 role的对应关系
        userAndUserGroupMap.forEach((userId, groupId) -> groupId.forEach(id -> {
            Set<Long> roleIds = userGoupAndRoleIdMap.get(id);
            roleIds = Optional.ofNullable(roleIds).orElse(new HashSet<>(0));
            roleIds.forEach(roleId -> {
                String roleName = userRoleNameMap.get(roleId);
                if (userAndRoleNameMap.containsKey(userId)) {
                    userAndRoleNameMap.get(userId).add(roleName);
                } else {
                    HashSet<String> roleNames = new HashSet<>(1);
                    roleNames.add(roleName);
                    userAndRoleNameMap.put(userId, roleNames);
                }
            });
        }));

        users.forEach(user -> {
            if (userAndRoleNameMap.containsKey(user.getId())) {
                Set<String> roleNames = user.getRoleNames();
                Set<String> newRoleNames = userAndRoleNameMap.get(user.getId());
                if (roleNames == null) {
                    user.setRoleNames(newRoleNames);
                } else {
                    roleNames.addAll(newRoleNames);
                    user.setRoleNames(roleNames);
                }

            }
        });
    }

    /**
     * 功能描述:
     * 〈 根据用户的角色设置用户角色名称 〉
     *
     * @param users             用户集合
     * @param userAndRoleDOList 用户和角色的集合
     * @param roleMap           角色map
     * @return : void
     * @author : yls
     * @date : 2022/2/16 14:20
     */
    private void setUserRoleNames(List<UserDO> users, List<UserAndRoleDO> userAndRoleDOList, Map<Long, String> roleMap) {
        // 设置用户角色名称
        Map<Long, Set<String>> userRoleMap = new HashMap<>(10);
        userAndRoleDOList.forEach(userRoleDO -> {
            if (roleMap.containsKey(userRoleDO.getRoleId())) {
                Long userId = userRoleDO.getUserId();
                if (!userRoleMap.containsKey(userId)) {
                    userRoleMap.put(userId, new HashSet<>(5));
                }
                userRoleMap.get(userId).add(roleMap.get(userRoleDO.getRoleId()));
            }
        });
        /*
         * 组装用户的角色
         * 包括用户所属角色
         * 包括用户所属用户组的所属角色
         */
        users.forEach(user -> {
            if (userRoleMap.containsKey(user.getId())) {
                Set<String> roleNames = user.getRoleNames();
                Set<String> newRoleNames = userRoleMap.get(user.getId());
                if (roleNames == null) {
                    user.setRoleNames(newRoleNames);
                } else {
                    roleNames.addAll(newRoleNames);
                    user.setRoleNames(roleNames);
                }

            }
        });
    }

    /**
     * 功能描述:
     * 〈 设置用户组信息 〉
     *
     * @param users               用户组
     * @param userAndUserGroupMap 用户和用户组关联关系
     * @param userGroupNameMap    用户组名称map
     * @return : void
     * @author : yls
     * @date : 2022/2/16 14:21
     */
    private void setUserGroupNames(List<UserDO> users, Map<Long, Set<Long>> userAndUserGroupMap, Map<Long, String> userGroupNameMap) {
        Map<Long, Set<String>> userAndUserGroupNameMap = new HashMap<>(1);
        userAndUserGroupMap.forEach((userId, groupIds) -> groupIds.forEach(groupId -> {
            if (userAndUserGroupNameMap.containsKey(userId)) {
                userAndUserGroupNameMap.get(userId).add(userGroupNameMap.get(groupId));
            } else {
                Set<String> groupNames = new HashSet<>();
                groupNames.add(userGroupNameMap.get(groupId));
                userAndUserGroupNameMap.put(userId, groupNames);
            }
        }));
        users.forEach(user -> {
            if (userAndUserGroupNameMap.containsKey(user.getId())) {
                user.setUserGroupNames(userAndUserGroupNameMap.get(user.getId()));
            }
        });
    }

    @Override
    @Transactional(readOnly = true)
    public Set<String> listPermissionsByUserName(String userName) {
        return new HashSet<>();
    }


    @Override
    @Transactional(readOnly = true)
    public UserDO getUser(Long id) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserDO::getId, id)
                .select(UserDO.class, field -> !field.getColumn().equals(BusinessConstant.PASSWORD))
                .last(BusinessConstant.LIMIT_ONE);
        return getOne(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public UserDO updatePassword(UserDetails userDetails, String newPassword) throws ServiceException {
        String password = userDetails.getPassword();
        // RSA 解密
        password = RsaUtils.decrypt(password, rsaKeyConfig.getPrivateKeyFile());
        newPassword = RsaUtils.decrypt(newPassword, rsaKeyConfig.getPrivateKeyFile());
        String username = SecurityUtils.getUsername();
        // 查询数据库用户信息
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserDO::getUsername, username)
                .last(BusinessConstant.LIMIT_ONE);
        UserDO userDO = getOne(queryWrapper);
        // 验证原密码
        boolean flag = (password == null || !new BCryptPasswordEncoder().encode(password).equals(userDO.getPassword()));
        if (Boolean.TRUE.equals(flag)) {
            throw new ServiceException(ServiceExceptionEnum.SOURCE_PASSWORD_ERROR);
        }
        userDO.setPassword(newPassword);
        updateUser(userDO);
        return userDO;
    }

    @Override
    @Transactional(readOnly = true)
    public UserDO getUserBaseInfo(String userName) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserDO::getUsername, userName)
                .select(UserDO.class, field -> !field.getColumn().equals(BusinessConstant.PASSWORD))
                .last(BusinessConstant.LIMIT_ONE);
        return getOne(queryWrapper);
    }

}
